/*
 * MetadataPanel.java
 *
 *  created: 7.10.2011
 *  charset: UTF-8
 *  license: MIT (X11) (See LICENSE file for full license)
 */
package cz.mp.k3bg.gui;


import cz.mp.k3bg.Constants;
import cz.mp.k3bg.Images;
import static cz.mp.k3bg.Images.*;
import static cz.mp.k3bg.TextSource.getLocText;
import cz.mp.k3bg.core.Metadata;
import cz.mp.k3bg.core.OpfMetadataReader;
import cz.mp.k3bg.gui.component.LabelForMandatory;
import cz.mp.k3bg.gui.component.LinkLabel;
import cz.mp.k3bg.gui.component.TextPrompt;
import cz.mp.k3bg.gui.helper.FileChooserBuilder;
import cz.mp.k3bg.log.LoggerManager;
import cz.mp.k3bg.misc.DefaultLocComparator;
import cz.mp.k3bg.util.DesktopUtils;
import cz.mp.util.DateUtils;
import cz.mp.util.GuiUtils;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.*;
import java.util.logging.Logger;
import javax.swing.*;
import javax.xml.stream.XMLStreamException;
import net.miginfocom.swing.MigLayout;

// pozor: Java: cs_CZ;  RFC 3066: cs (neexistuje cs-CZ);  viz RFC 3066 !

/**
 * Panel pro zadání informací o knize (metadat).
 * 
 * @author Martin Pokorný
 * @version 0.1
 * @see MainTabbedPane
 * @see Metadata
 */
public class MetadataPanel extends TabPanel {

    private static final boolean DEBUG = false;
    private static final Logger logger =
            LoggerManager.getLogger(MetadataPanel.class, DEBUG);
    
    private ImageIcon IMG_INFO = Images.getImage(INFO_32);

    private JButton importFromOpfButton = new JButton(
            getLocText("gui.metadata.import_from_opf"));
    private JFileChooser opfFileChooser;
            
    private JLabel authorLabel = new LabelForMandatory(getLocText("gui.metadata.author"));
    private JTextField authorField = new JTextField();

    private JLabel titleLabel = new LabelForMandatory(getLocText("gui.metadata.title"));
    private JTextField titleField = new JTextField();

    private JLabel langLabel = new LabelForMandatory(getLocText("gui.metadata.lang"));
    private JComboBox langCombo = new JComboBox();    
    private LinkLabel langCodesLink = new LinkLabel(getLocText("gui.metadata.lang.codes"));
    
    private JLabel idLabel = new LabelForMandatory(getLocText("gui.metadata.id"));
    private JTextField idField = new JTextField();
    private JCheckBox idIsbnCheck = new JCheckBox(getLocText("gui.metadata.id.isbn"));

    private JLabel genreLabel = new JLabel(getLocText("gui.metadata.genre"));
    private JComboBox genreCombo = new JComboBox();

    private JLabel publisherLabel = new JLabel(getLocText("gui.metadata.publisher"));
    private JTextField publisherField = new JTextField();

    private JLabel publishDateLabel = new JLabel(getLocText("gui.metadata.publish_date"));
    /** Datum vydání ve formátu {@literal YYYY-MM-DD}. */
    private JTextField publishDateField = new JTextField();
    private JButton publishDateTodayButton = new JButton(getLocText("gui.metadata.today"));
    private TextPrompt publishDateFormatPrompt = new TextPrompt(getLocText("gui.metadata.publish_date.format_prompt"));
    
    private JLabel descriptionLabel = new JLabel(getLocText("gui.metadata.description"));
    private JTextPane descriptionArea = new JTextPane();
    private JScrollPane descriptionScrollArea = new JScrollPane(descriptionArea);


    /** */
    private static final Map<String, String> GENRES;
    static {
        Map<String, String> allGenres = new HashMap<String, String>();
        allGenres.put("Antiques & Collectibles",  getLocText("genre.Antiques_&_Collectibles"));
        allGenres.put("Architecture",  getLocText("genre.Architecture"));
        allGenres.put("Art",  getLocText("genre.Art"));
        allGenres.put("Bibles",  getLocText("genre.Bibles"));
        allGenres.put("Biography & Autobiography",  getLocText("genre.Biography_&_Autobiography"));
        allGenres.put("Body, Mind & Spirit",  getLocText("genre.Body,_Mind_&_Spirit"));
        allGenres.put("Business & Economics",  getLocText("genre.Business_&_Economics"));
        allGenres.put("Comics & Graphic Novels",  getLocText("genre.Comics_&_Graphic_Novels"));
        allGenres.put("Computers",  getLocText("genre.Computers"));
        allGenres.put("Cooking",  getLocText("genre.Cooking"));
        allGenres.put("Crafts & Hobbies",  getLocText("genre.Crafts_&_Hobbies"));
        allGenres.put("Design",  getLocText("genre.Design"));
        allGenres.put("Drama",  getLocText("genre.Drama"));
        allGenres.put("Education",  getLocText("genre.Education"));
        allGenres.put("Family & Relationships",  getLocText("genre.Family_&_Relationships"));
        allGenres.put("Fiction",  getLocText("genre.Fiction"));
        allGenres.put("Foreign Language Study",  getLocText("genre.Foreign_Language_Study"));
        allGenres.put("Games",  getLocText("genre.Games"));
        allGenres.put("Gardening",  getLocText("genre.Gardening"));
        allGenres.put("Health & Fitness",  getLocText("genre.Health_&_Fitness"));
        allGenres.put("History",  getLocText("genre.History"));
        allGenres.put("House & Home",  getLocText("genre.House_&_Home"));
        allGenres.put("Humor",  getLocText("genre.Humor"));
        allGenres.put("Juvenile Fiction",  getLocText("genre.Juvenile_Fiction"));
        allGenres.put("Juvenile Nonfiction",  getLocText("genre.Juvenile_Nonfiction"));
        allGenres.put("Language Arts & Disciplines",  getLocText("genre.Language_Arts_&_Disciplines"));
        allGenres.put("Law",  getLocText("genre.Law"));
        allGenres.put("Literary Collections",  getLocText("genre.Literary_Collections"));
        allGenres.put("Literary Criticism",  getLocText("genre.Literary_Criticism"));
        allGenres.put("Mathematics",  getLocText("genre.Mathematics"));
        allGenres.put("Medical",  getLocText("genre.Medical"));
        allGenres.put("Music",  getLocText("genre.Music"));
        allGenres.put("Nature",  getLocText("genre.Nature"));
        allGenres.put("Performing Arts",  getLocText("genre.Performing_Arts"));
        allGenres.put("Pets",  getLocText("genre.Pets"));
        allGenres.put("Philosophy",  getLocText("genre.Philosophy"));
        allGenres.put("Photography",  getLocText("genre.Photography"));
        allGenres.put("Poetry",  getLocText("genre.Poetry"));
        allGenres.put("Political Science",  getLocText("genre.Political_Science"));
        allGenres.put("Psychology",  getLocText("genre.Psychology"));
        allGenres.put("Reference",  getLocText("genre.Reference"));
        allGenres.put("Religion",  getLocText("genre.Religion"));
        allGenres.put("Science",  getLocText("genre.Science"));
        allGenres.put("Self-Help",  getLocText("genre.Self-Help"));
        allGenres.put("Social Science",  getLocText("genre.Social_Science"));
        allGenres.put("Sports & Recreation",  getLocText("genre.Sports_&_Recreation"));
        allGenres.put("Study Aids",  getLocText("genre.Study_Aids"));
        allGenres.put("Technology & Engineering",  getLocText("genre.Technology_&_Engineering"));
        allGenres.put("Transportation",  getLocText("genre.Transportation"));
        allGenres.put("Travel",  getLocText("genre.Travel"));
        allGenres.put("True Crime",  getLocText("genre.True_Crime"));
        GENRES = Collections.unmodifiableMap(allGenres);
    }


    // -----

    /** */
    public MetadataPanel() {
        super();
        initComponents();
        initLayout();
        initEventHandlers();
    }


    /** */
    private void initComponents() {
        logger.finest("");

        authorLabel.setLabelFor(authorField);
        titleLabel.setLabelFor(titleField);
        langLabel.setLabelFor(langCombo);
        idLabel.setLabelFor(idField);

        genreLabel.setLabelFor(genreCombo);
        publisherLabel.setLabelFor(publisherField);
        publishDateLabel.setLabelFor(publishDateField);
        descriptionLabel.setLabelFor(descriptionScrollArea);

        publishDateFormatPrompt.setToComponent(publishDateField);
                
        idIsbnCheck.setToolTipText(getLocText("gui.metadata.id.isbn.tooltip"));
        importFromOpfButton.setToolTipText(getLocText("gui.metadata.import_from_opf.tooltip"));

        // -----
        
        idIsbnCheck.setSelected(true);
        
        GuiUtils.adjustBackground(descriptionArea);
        descriptionScrollArea.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        descriptionScrollArea.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        
        // -----
        
        langCombo = new JComboBox(getLangsForCombo());
        langCombo.setEditable(true);
        setLocaleInLangInCombo(Locale.getDefault());

        langCodesLink.setToolTipText(Constants.RFC_3066_URL);
                
        genreCombo = new JComboBox(getGenresForCombo());
        genreCombo.setEditable(true);
        genreCombo.setSelectedItem("");
        

        FileChooserBuilder fchb = new FileChooserBuilder();
        fchb.setFilter(Constants.OPF_FILEFILTER);
        fchb.setSelectionMode(JFileChooser.FILES_ONLY);
        opfFileChooser = fchb.getFileChooser();
    }
    
    /** */
    private void initLayout() {
        logger.finest("");
        this.setLayout(new MigLayout("",
                "unrel[label]rel[fill,grow,75:45%:45%]rel[]rel[fill,grow]unrel",
                "unrel[]rel[]"));

        this.add(titleLabel);
        this.add(titleField, "sg");

        this.add(importFromOpfButton, "sgy, right, span, wrap");

        this.add(authorLabel);
        this.add(authorField, "sg, wrap");

        this.add(idLabel);
        this.add(idField, "sg");
        this.add(idIsbnCheck, "wrap");

        this.add(langLabel);
        this.add(langCombo, "sg");
        this.add(langCodesLink, "wrap");
        
        this.add(genreLabel);
        this.add(genreCombo, "sg, wrap");

        this.add(publisherLabel);
        this.add(publisherField, "sg, wrap");

        this.add(publishDateLabel);
        this.add(publishDateField, "sg");
        this.add(publishDateTodayButton, "sgy, wrap unrel");        

        this.add(descriptionLabel, "wrap rel");
        this.add(descriptionScrollArea, "span, grow, w 75:300:max, h 50:135:max, wrap 2*para");

        this.add(statusPanel, "spanx, grow");
    }
    
    /** */
    private void initEventHandlers() {
        logger.finest("");
        
        importFromOpfButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                importFromOpf();
            }
        });
        
        publishDateTodayButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                publishDateField.setText(
                        DateUtils.formatToYyyyMmDd(new java.util.Date()));
            }
        });
        
        langCodesLink.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    GuiUtils.setWaitCursorForAmoment(MainFrame.getInstance());
                    DesktopUtils.openBrowser(Constants.RFC_3066_URL);
                } catch (MalformedURLException ex) {
                    logger.warning(ex.toString());
                }
            }
        });
    }   
    
    /**
     * 
     */
    private void importFromOpf() {

        if (opfFileChooser.showOpenDialog(MetadataPanel.this) != 
                JFileChooser.APPROVE_OPTION) {
            return;
        }

        File file = opfFileChooser.getSelectedFile();

        OpfMetadataReader opfMetaReader = new OpfMetadataReader(file);
        try {
            opfMetaReader.parse();
            Metadata metadata = opfMetaReader.getReadedMetadata();

            fillForm(metadata);
            
        } catch (XMLStreamException ex) {
            logger.warning(ex.toString());
            if (DEBUG) {  ex.printStackTrace();  }
            Dialogs.showErrorDialog(ex);
        } catch (IOException ex) {
            logger.warning(ex.toString());
            if (DEBUG) {  ex.printStackTrace();  }
            Dialogs.showErrorDialog(ex);
        } finally {
            try {
                opfMetaReader.close();
            } catch (XMLStreamException ex) {
                logger.warning(ex.toString());
                if (DEBUG) {  ex.printStackTrace();  }
                Dialogs.showErrorDialog(ex);
            } catch (IOException ex) {
                logger.warning(ex.toString());
                if (DEBUG) {  ex.printStackTrace();  }
                Dialogs.showErrorDialog(ex);
            }
        }        
    }
    
    /**
     * 
     * @param metadata 
     */
    public void fillForm(Metadata metadata) {
        clear();
        if (metadata == null) {
            return;
        }

        titleField.setText(metadata.getTitle());
        authorField.setText(metadata.getAuthor());
        idField.setText(metadata.getId());
        idIsbnCheck.setSelected(metadata.isIdIsIsbn());
        setLocaleInLangInCombo(getLocale(metadata.getLang()));
        genreCombo.setSelectedItem(metadata.getGenre());
        publisherField.setText(metadata.getPublisher());
        publishDateField.setText(metadata.getPublishDate());
        descriptionArea.setText(metadata.getDescription());
    }

    /**
     * 
     */
    @Override
    public void clear() {
        titleField.setText("");
        authorField.setText("");
        idField.setText("");
        idIsbnCheck.setSelected(true);
        setLocaleInLangInCombo(Locale.getDefault());
        genreCombo.setSelectedItem("");
        publisherField.setText("");
        publishDateField.setText("");
        descriptionArea.setText("");
        
        update();
    }

    /**
     * 
     *
     * @return  {@code null}, pokud nejsou zadány povinné údaje
     *      (titul, autor, id, jazyk), jinak vrátí instanci
     *      {@linkplain Metadata}.
     */
    Metadata createMetadata() {
        String title = titleField.getText().trim();
        String author = authorField.getText().trim();
        String id = idField.getText().trim();
        String lang = getSelectedLang();
        // lang - v OPF se očekává  cs, en-US (ne cs_CZ, ne cs-CZ, ne en_US, ...)

        if (title.isEmpty()
                || author.isEmpty()
                || id.isEmpty()
                || lang.isEmpty()) {
            return null;
        }
        else {
            Metadata result = new Metadata();
            
            result.setTitle(title);
            result.setAuthor(author);
            result.setLang(lang);
            result.setId(id);
            result.setIdIsIsbn(idIsbnCheck.isSelected());

            result.setGenre(getSelectedGenre());
            result.setPublisher(publisherField.getText().trim());
            result.setPublishDate(publishDateField.getText().trim());
            result.setDescription(descriptionArea.getText());

            return result;
        }
    }
    
    /**
     * 
     */
    private void setLocaleInLangInCombo(Locale locale) {
        if (locale != null) {
            langCombo.setSelectedItem(formatLangForCombo(locale));
        }
        else {
            logger.info("locale=null");
        }
    }
    
    /**
     * 
     * @param localeText
     * @return 
     */
    private Locale getLocale(String localeText) {
        String[] sss = localeText.split("_");
        if (sss.length == 1) {
            return new Locale(sss[0]);
        }
        if (sss.length == 2) {
            return new Locale(sss[0], 
                    sss[1].toUpperCase());
        }
        if (sss.length == 3) {
            return new Locale(sss[0], 
                    sss[1].toUpperCase(), sss[2].toUpperCase());
        }
        
        return null;
    }
        
    /**
     * 
     * @return 
     */
    private String getSelectedLang() {
        String selected = (String) langCombo.getSelectedItem();
        return selected.trim();
    }
    
    /**
     * 
     * @return 
     */
    private String[] getLangsForCombo() {
        Locale locales[] = Locale.getAvailableLocales();
        Collection<String> langs = 
                new TreeSet<String>(
                        new DefaultLocComparator<String>());
        for (Locale locale : locales) {
            langs.add(formatLangForCombo(locale));
        }
        return langs.toArray(new String[0]);
    }
    
    /**
     * 
     * @param locale
     * @return 
     */
    private String formatLangForCombo(Locale locale) {
        return locale.getLanguage();
    }    
    
    /**
     * 
     * @return 
     */
    private String[] getGenresForCombo() {        
        Collection<String> genres = 
                new TreeSet<String>(
                        new DefaultLocComparator<String>());
        for (String key : GENRES.keySet()) {
            //genres.add(formatGenreForCombo(key, GENRES.get(key)) );
            genres.add(key);
        }
        return genres.toArray(new String[0]);
    }
    
    /**
     * 
     * @return 
     */
    private String getSelectedGenre() {
        String selected = (String) genreCombo.getSelectedItem();
        
        int open = selected.indexOf("(");
        int close = selected.lastIndexOf(")");
        if (open >= 0 && close >= 0)  {
            String genre = selected.substring(open+1, close);            
            if (GENRES.containsKey(genre)) {
                return genre;
            }
            return selected;
        }
        else {
            return selected;
        }     
    }
                
    @Override
    public ImageIcon getImageIcon() {
        return IMG_INFO;
    }

    @Override
    public String getTitle() {
        return getLocText("gui.maintabpane.metadata");
    }

}   // MetadataPanel.java

